﻿#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers.
#include <iostream>
#include <ostream>
#endif

#include <windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <D3Dcompiler.h>
#include <DirectXMath.h>
#include "d3dx12.h"

#pragma region 程序窗口相关

///window句柄
HWND hwnd = NULL;

///窗口标题
LPCTSTR WindowTitle = L"Bz Window";

///窗口名称
LPCTSTR WindowName = L"BzTutsApp";

///窗口宽高
int width = 800;
int height = 600;

///是否全屏
bool fullScreen = false;

///我们会在Running为false的时候终止城管希
bool Running = true;

int EventDispatchCount = 0;

///用户事件回调，初始化完成时会自动调用，注意hWnd参数防止递归
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

///创建一个窗口
bool InitializeWindow(HINSTANCE hInstance, int showWnd, int width, int height, bool fullScreen);

///应用主循环
void MainLoop();

#pragma endregion

#pragma region Direct3D 12相关

///只会在对象存在的情况下调用release，防止不存在的对象上调用release而发生异常
#define SAFE_RELEASE(p) { if ( (p) ) { (p)->Release(); (p) = 0; } }

///帧缓冲个数
const int frameBufferCount = 3;

///direct3D decive
ID3D12Device* device;

///用于在render targets之间进行切换的交换链
IDXGISwapChain3* swapChain;

///命令队列的容器
ID3D12CommandQueue* commandQueue;

///用于持有例如渲染对象之类的描述符堆
ID3D12DescriptorHeap* rtvDescriptorHeap;

///render target的数量应当等于缓冲区数量
ID3D12Resource* renderTargets[frameBufferCount];

///命令分配器数量应当为缓冲区数量*线程数量（这里我们只有一个主线程，所以就是3*1 = 3）
ID3D12CommandAllocator* commandAlloactor[frameBufferCount];

///我们可以在一个命令列表中存储命令信息，然后执行他们来渲染这一帧
ID3D12GraphicsCommandList* commandList;

///当我们的渲染队列正在被GPU执行，可以用隔离锁住它，我们需要和命令分配器一样多的隔离（如果我们想知道何时完成一个asset的详细信息，可以多加几个）
ID3D12Fence* fence[frameBufferCount];

///当我们的隔离被GPU解锁时会抛出事件，这是一个句柄来处理那个事件
HANDLE fenceEvent;

///这个值每帧都会增加，每个隔离都会有他自己的值
UINT64 fenceValue[frameBufferCount];

///包含管线状态的pso
ID3D12PipelineState* pipelineStateObject;

///根签名定义将要访问的shader数据
ID3D12RootSignature* rootSignature;

///光栅器将会拉伸到的区域
D3D12_VIEWPORT viewport;

///绘制的区域，在此区域外的像素将不会被绘制
D3D12_RECT scissorRect;

///默认的 GPU内存部分的缓冲区，我们将会为我们传递进来的三角形加载顶点数据到它里面
ID3D12Resource* vertexBuffer;

///一个结构体，包含一个指向GPU内存区顶点数据的指针，缓冲区的总大小，以及每个顶点的大小
D3D12_VERTEX_BUFFER_VIEW vertexBufferView;

///一个在GPU端的默认缓冲区，我们将会在这存放我们的三角形索引数据
ID3D12Resource* indexBuffer;

///一个用于持有索引缓冲区信息的结构体
D3D12_INDEX_BUFFER_VIEW indexBufferView;

///深度模板缓冲区
ID3D12Resource* depthStencilBuffer;

///深度木板描述符堆
ID3D12DescriptorHeap* dsDescriptorHeap;

///存储持有我们Constant Buffer的描述符
ID3D12DescriptorHeap* mainDescriptorHeap[frameBufferCount];

///这是一块GPU上的内存，我们的constant Buffer将会放在这里
ID3D12Resource* constantBufferUploadHeap[frameBufferCount];

///constant Buffer结构
struct ConstantBuffer
{
    DirectX::XMFLOAT4 colorMultiplier;
};

///将会被发往gpu的constant buffer
ConstantBuffer cbColorMultiplierData;

///索引GPU端constant BUFFER内存位置的指针
UINT8* cbColorMultiplierGPUAddress[frameBufferCount];

///顶点数据结构
struct Vertex
{
    Vertex(float x, float y, float z, float r, float g, float b, float a): pos(x, y, z), color(r, g, b, a)
    {
    }

    DirectX::XMFLOAT3 pos;
    DirectX::XMFLOAT4 color;
};

///当前我们正处于的rtv的索引
int frameIndex;

///rtv描述符在device上的大小（所有的前缓冲区和后缓冲区都会有一样大小）
int rtvDescriptorSize;


//方法声明
///初始化D3D
bool InitD3D();

///更新游戏逻辑
void Update();

///更新direct3D管线（更新命令列表）
void UpdatePipeline();

///执行命令列表
void Render();

///释放一些对象，并且清理内存
void Cleanup();

///等待，直到GPU完成命令列表的执行
void WaitForPreviousFrame();

#pragma endregion
